﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.IO;
using System.Configuration;
using Valentia.Kinection.Remoting.Helper;
using Valentia.Kinection.DataTypes;
using Valentia.Kinection.Configuration.Manager;
using System.ServiceProcess;

namespace Valentia.Kinection.Service.Manager
{
    /// <summary>
    /// This is the main class of service manager which hosts the
    /// forms and other functionalities
    /// </summary>
    public partial class ServiceManager : Form
    {
        #region Constants

        /// <summary>
        /// Balloon tip visible time out in milliseconds
        /// </summary>
        private const int TOOLTIP_VISIBLE_TIMEOUT = 5000;

        /// <summary>
        /// Balloon tip Exception Notification Title
        /// </summary>
        private const String BALLOON_TIP_EXCEPTION = "Exception";

        /// <summary>
        /// Balloon tip Data Extractor Notification Title
        /// </summary>
        private const String BALLOON_TIP_DATA_EXTRACTOR = "Data Extractor";

        /// <summary>
        /// Balloon tip Kinect Device Notification Title
        /// </summary>
        private const String BALLOON_TIP_KINECT_DEVICE = "Kinect Device";

        /// <summary>
        /// Balloon tip Skeleton Notification Title
        /// </summary>
        private const String BALLOON_TIP_SKELETON = "Skeleton";

        /// <summary>
        /// Kinect windows service configuration application name
        /// </summary>
        private const String KINECT_SERVER_CONFIG_APP_NAME = "MS Kinect Service Configuration Manager.exe";

        #endregion

        #region Declarations

        /// <summary>
        /// Will contain kinect service settings application name
        /// </summary>
        private static readonly String ConfigAppExeName = String.Empty;

        /// <summary>
        /// Remoting client object to communicate with Remoting server
        /// </summary>
        private Client remotingClient = null;

        /// <summary>
        /// Data table to maintain history of notifications sent by Kinect windows service
        /// </summary>
        private DataTable notificationHistoryTable = null;

        /// <summary>
        /// Maintains the request status sent to Kinect windows service
        /// </summary>
        private RemoteRequestStatus remoteRequestStatus = RemoteRequestStatus.NotRequested;

        /// <summary>
        /// Remoting server URI
        /// </summary>
        private String serverURI = string.Empty;

        /// <summary>
        /// Service manager login name for authentication
        /// </summary>
        private String loginName = string.Empty;

        /// <summary>
        /// Service manager password for authentication
        /// </summary>
        private String password = string.Empty;

        /// <summary>
        /// Service manager remoting client name
        /// </summary>
        private String clientName = string.Empty;

        /// <summary>
        /// Service manager remoting client port
        /// </summary>
        private int portNo = 0;

        /// <summary>
        /// Interval to check remoting connection status
        /// </summary>
        private int connectionStatusInterval = 0;

        /// <summary>
        /// Interval to check kinect service status
        /// </summary>
        private int kinectServiceStatusInterval = 0;

        /// <summary>
        /// Timeout interval of client request
        /// </summary>
        private int clientRequestTimeoutInterval = 0;

        /// <summary>
        ///  Used for request timeout
        /// </summary>
        private EventWaitHandle requestTimeoutWaitHandle = null;

        /// <summary>
        /// Timer used to update kinect service status
        /// </summary>
        private System.Timers.Timer updateKinectServiceStatus = null;

        /// <summary>
        /// Log directory path
        /// </summary>
        private String logFolder = String.Empty;

        /// <summary>
        /// Log is enabled/disabled
        /// </summary>
        private bool loggingEnabled = true;

        #endregion

        #region Constructor

        /// <summary>
        /// Static Constructor.
        /// </summary>
        static ServiceManager()
        {
            ServiceManager.ConfigAppExeName 
                = typeof(frmConfigManager).Assembly.GetName().Name + ".exe";
        }
      
        /// <summary>
        /// Constructor.
        /// </summary>
        public ServiceManager()
        {
            InitializeComponent();

            // Registering Application level exceptions event
            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

            this.ntfyIcnKinectServiceManager.Visible = true;

            // Initializing notification history datatable and specifying schema
            this.notificationHistoryTable = new DataTable();
            this.notificationHistoryTable.Columns.Add(new DataColumn("NotificationDateTime", System.Type.GetType("System.DateTime")));
            this.notificationHistoryTable.Columns.Add(new DataColumn("NotificationType", System.Type.GetType("System.String")));
            this.notificationHistoryTable.Columns.Add(new DataColumn("NotificationMessage", System.Type.GetType("System.String")));

            this.LoadApplicationSettings();

            RefreshServiceStatus();

            // Initializing the timer to monitor and update the kinect service status
            updateKinectServiceStatus = new System.Timers.Timer();
            updateKinectServiceStatus.Elapsed += new System.Timers.ElapsedEventHandler(updateKinectServiceStatus_Elapsed);
            updateKinectServiceStatus.Interval = this.kinectServiceStatusInterval * 1000;
            updateKinectServiceStatus.Enabled = true;
            updateKinectServiceStatus.Start();

            // Intializating the remoting client and binding its event
            this.remotingClient = new Client(this.clientName, this.loginName, this.password, this.serverURI, this.portNo, this.connectionStatusInterval);
            this.remotingClient.ClientConnectivityInfoEvent += new Client.ClientConnectivityInfoEventDlg(remotingClient_evtClientConnectivityInfoEvent);
            this.remotingClient.DeviceConnectivityInfoEvent += new Client.DeviceConnectivityInfoEventDlg(remotingClient_evtDeviceConnectivityInfoEvent);
            this.remotingClient.ConnectionStatusInfoEvent += new Client.ConnectionStatusEventDlg(remotingClient_ConnectionStatusInfoEvent);
            this.remotingClient.ShowNotificationReceived += new EventHandler<NotificationReceivedArgs>(remotingClient_ShowNotificationReceived);
            this.remotingClient.ServerConnectionStatus += new EventHandler(remotingClient_ServerConnectionStatus);

            ThreadPool.QueueUserWorkItem(state => { try { this.remotingClient.Connect(); } catch { } }, null);

            // Initializting the request timeout EventWaitHandle
            requestTimeoutWaitHandle = new AutoResetEvent(false);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Loads service manager application settings from app.config
        /// </summary>
        private void LoadApplicationSettings()
        {
            try
            {
                System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                ConfigurationManager.RefreshSection("appSettings");

                this.clientName = config.AppSettings.Settings["ClientName"].Value;
                this.loginName = config.AppSettings.Settings["LoginName"].Value;
                this.password = config.AppSettings.Settings["Password"].Value;
                this.serverURI = config.AppSettings.Settings["ServerURI"].Value;
                this.portNo = Convert.ToInt32(config.AppSettings.Settings["ServerPort"].Value);
                this.connectionStatusInterval = Convert.ToInt32(config.AppSettings.Settings["ConnectionTestInterval"].Value);
                this.kinectServiceStatusInterval = Convert.ToInt32(config.AppSettings.Settings["KinectServiceStatusInterval"].Value);
                this.clientRequestTimeoutInterval = Convert.ToInt32(config.AppSettings.Settings["ClientRequestTimeoutInterval"].Value);

                this.logFolder = config.AppSettings.Settings["LogDirectory"].Value;
                this.loggingEnabled = Convert.ToBoolean(config.AppSettings.Settings["LogEnabled"].Value); ;

            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in LoadApplicationSettings(). {0}", exp.Message); }            
        }

        /// <summary>
        /// Changes the application icon and tool tip in the notification area
        /// </summary>
        /// <param name="applicationStatusIcon">Application Icon type</param>
        /// <param name="notifyingToolTip">Tooltip</param>
        private void ChangeApplicationStatusIcon(ApplicationStatusIcon applicationStatusIcon, String notifyingToolTip)
        {
            try
            {
                MethodInvoker UpdateSubItem = delegate
                {
                    switch (applicationStatusIcon)
                    {
                        case ApplicationStatusIcon.ServerDisconnected:
                            ntfyIcnKinectServiceManager.Icon = Properties.Resources.BothDisconnected;
                            ntfyIcnKinectServiceManager.Text = Program.SysTrayName + " (UI Client Offline)";
                            break;
                        case ApplicationStatusIcon.ClientConnected:
                            ntfyIcnKinectServiceManager.Icon = Properties.Resources.ClientConnected;
                            ntfyIcnKinectServiceManager.Text = notifyingToolTip;
                            break;
                        case ApplicationStatusIcon.DeviceConnected:
                            ntfyIcnKinectServiceManager.Icon = Properties.Resources.DeviceConnected;
                            ntfyIcnKinectServiceManager.Text = notifyingToolTip;
                            break;
                        case ApplicationStatusIcon.BothConnected:
                            ntfyIcnKinectServiceManager.Icon = Properties.Resources.BothConnected;
                            ntfyIcnKinectServiceManager.Text = notifyingToolTip;
                            break;
                        case ApplicationStatusIcon.BothDisconnected:
                            ntfyIcnKinectServiceManager.Icon = Properties.Resources.BothDisconnected;
                            ntfyIcnKinectServiceManager.Text = notifyingToolTip;
                            break;
                    }
                };
                this.Invoke(UpdateSubItem);
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in ChangeApplicationStatusIcon(). {0}", exp.Message); }
        }

        /// <summary>
        /// Displays a balloon tip in the taskbar
        /// </summary>
        /// <param name="tipTitle">Balloon tip title</param>
        /// <param name="tipText">Balloon tip text</param>
        /// <param name="tipIcon">Balloon tip icon type</param>
        private void ShowBalloonTip(String tipTitle, String tipText, ToolTipIcon tipIcon)
        {
            try
            {
                lock (notificationHistoryTable)
                {
                    notificationHistoryTable.Rows.Add(new string[] { DateTime.Now.ToString("s"), tipTitle, tipText });
                }

                MethodInvoker UpdateSubItem = delegate
                {
                    ntfyIcnKinectServiceManager.BalloonTipTitle = tipTitle;
                    ntfyIcnKinectServiceManager.BalloonTipText = tipText;
                    ntfyIcnKinectServiceManager.BalloonTipIcon = tipIcon;
                    ntfyIcnKinectServiceManager.ShowBalloonTip(TOOLTIP_VISIBLE_TIMEOUT);
                };
                this.Invoke(UpdateSubItem);
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in ShowBalloonTip(). {0}", exp.Message); }

        }

        /// <summary>
        /// Enables or Disables the context menu items
        /// </summary>
        /// <param name="enableMenuItem">Enable/Disable context menu items</param>
        private void SetMenuItemsStatus(bool enableMenuItem)
        {
            try
            {
                if (this.InvokeRequired)
                {
                    MethodInvoker UpdateSubItem = delegate { this.SetMenuItemsStatus(enableMenuItem); };
                    this.Invoke(UpdateSubItem);
                    return;
                }

                menuItemExit.Enabled = enableMenuItem;
                menuItemShowNotificationHistory.Enabled = enableMenuItem;
                menuItemShowServiceClientsStatus.Enabled = enableMenuItem;
                menuItemDeviceConnectivityStatus.Enabled = enableMenuItem;

                serverSettingsApplicationToolStripMenuItem.Enabled = enableMenuItem;
                serviceManagerSettingsToolStripMenuItem.Enabled = enableMenuItem;
                menuItemAdjustKinectDevice.Enabled = enableMenuItem;

                if (enableMenuItem)
                {
                    // Because menuItemKinectServiceStatus enable/disable 
                    // also depends on kinect service status

                    RefreshServiceStatus(); 
                }
                else
                {
                    menuItemKinectServiceStatus.Enabled = enableMenuItem;
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in SetMenuItemsStatus(). {0}", exp.Message); }

        }

        /// <summary>
        /// Shows the form of service clients connectivity details
        /// </summary>
        /// <param name="objData">Service clients connectivity data </param>
        private void LaunchServiceClientsConnectivityInfo(object objData)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                DataTable serviceClientConnectivityInfo = objData as DataTable;

                using (ServiceClientsConnectivityInfo scInfo = new ServiceClientsConnectivityInfo(serviceClientConnectivityInfo))
                {
                    scInfo.ShowDialog();
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in LaunchServiceClientsConnectivityInfo(). {0}", exp.Message); }
            finally { SetMenuItemsStatus(true); }
        }

        /// <summary>
        /// Shows the form of device connectivity details
        /// </summary>
        /// <param name="objData">Device connectivity data </param>
        private void LaunchKinectDeviceConnectivityInfo(object objData)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                DataTable kinectDeviceConnectivityInfo = objData as DataTable;
                using (KinectDeviceConnectivityInfo kdInfo = new KinectDeviceConnectivityInfo(kinectDeviceConnectivityInfo))
                {
                    kdInfo.ShowDialog();
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in LaunchKinectDeviceConnectivityInfo(). {0}", exp.Message); }
            finally { SetMenuItemsStatus(true); }
        }

        /// <summary>
        /// This method is used to check the status of Kinect windows service and
        /// update the toolstrip menu text according to the windows service status
        /// </summary>
        private void RefreshServiceStatus()
        {
            try
            {
                if (this.InvokeRequired)
                {
                    MethodInvoker UpdateSubItem = delegate { RefreshServiceStatus(); };
                    this.Invoke(UpdateSubItem);

                    return;
                }

                ServiceController myService = new ServiceController();

                myService.ServiceName = KinectService.ThisServiceName;


                if (myService.Status == ServiceControllerStatus.Running)
                {
                    menuItemKinectServiceStatus.Text = "&Stop Kinection Service";
                    menuItemKinectServiceStatus.Enabled = true;
                    return;
                }

                if (myService.Status == ServiceControllerStatus.Stopped)
                {
                    menuItemKinectServiceStatus.Text = "&Start Kinection Service";
                    menuItemKinectServiceStatus.Enabled = true;
                    return;
                }

                menuItemKinectServiceStatus.Text = "&Stop Kinection service";
                menuItemKinectServiceStatus.Enabled = false;


            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in RefreshServiceStatus(). {0}", exp.Message); }

        }

        #endregion

        #region Remoting Client Event Handlers

        /// <summary>
        /// Occurs when service client(s) connectivity information sent by kinect service
        /// </summary>
        /// <param name="clientConnectionInfo">List of clients with connectivity information</param>
        /// <param name="errorMessage">Error occurred in request processing</param>
        void remotingClient_evtClientConnectivityInfoEvent(List<ServiceClientInfo> clientConnectionInfo
            , String errorMessage)
        {
            try
            {
                if (errorMessage != null)// || !errorMessage.Equals(string.Empty))
                {
                    this.Log("remotingClient_evtClientConnectivityInfoEvent() Invoked! : Command response from remoting server, error message: {0}", errorMessage);

                    if (remoteRequestStatus == RemoteRequestStatus.RequestInProgress_ServiceClient)
                    {
                        remoteRequestStatus = RemoteRequestStatus.RequestFullFilled;
                        requestTimeoutWaitHandle.Set();
                    }

                    return;
                }

                if (remoteRequestStatus == RemoteRequestStatus.RequestInProgress_ServiceClient)
                {
                    remoteRequestStatus = RemoteRequestStatus.RequestFullFilled;
                    requestTimeoutWaitHandle.Set();
                }
                else
                {
                    return;
                }

                if (clientConnectionInfo != null)
                {
                    // Creating data table and specifying schema
                    DataTable serviceClientConnectivityInfo = new DataTable();

                    serviceClientConnectivityInfo.Columns.Add(new DataColumn("ClientType", System.Type.GetType("System.String")));
                    serviceClientConnectivityInfo.Columns.Add(new DataColumn("ClientID", System.Type.GetType("System.String")));
                    serviceClientConnectivityInfo.Columns.Add(new DataColumn("ClientName", System.Type.GetType("System.String")));
                    serviceClientConnectivityInfo.Columns.Add(new DataColumn("Connected", System.Type.GetType("System.String")));

                    // Populating data table from the list data sent by kinect service
                    foreach (ServiceClientInfo clientInfo in clientConnectionInfo)
                    {
                        serviceClientConnectivityInfo.Rows.Add(new string[] { clientInfo.ClientType.ToString()
                        ,clientInfo.ClientID, clientInfo.ClientName, clientInfo.Connected.ToString()});
                    }

                    // Launching service clients connectivity form in separte thread
                    ThreadPool.QueueUserWorkItem(new WaitCallback(LaunchServiceClientsConnectivityInfo)
                        , serviceClientConnectivityInfo);
                }
                else
                {
                    this.SetMenuItemsStatus(true);
                    // log/show error message
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in remotingClient_evtClientConnectivityInfoEvent(). {0}", exp.Message); }

        }

        /// <summary>
        /// Occurs when kinect device connectivity information sent by kinect service
        /// </summary>
        /// <param name="deviceConnectionInfo">List of devices with connectivity information</param>
        /// <param name="errorMessage">Error occurred in request processing</param>
        void remotingClient_evtDeviceConnectivityInfoEvent(List<KinectDeviceInfo> deviceConnectionInfo
            , String errorMessage)
        {
            try
            {
                if (errorMessage != null)//|| !errorMessage.Equals(string.Empty))
                {
                    this.Log("remotingClient_evtDeviceConnectivityInfoEvent() Invoked! : Command response from remoting server, error message: {0}", errorMessage);


                    if (remoteRequestStatus == RemoteRequestStatus.RequestInProgress_ServiceClient)
                    {
                        remoteRequestStatus = RemoteRequestStatus.RequestFullFilled;
                        requestTimeoutWaitHandle.Set();
                    }

                    return;
                }

                if (remoteRequestStatus == RemoteRequestStatus.RequestInProgress_KinectDevice)
                {
                    remoteRequestStatus = RemoteRequestStatus.RequestFullFilled;
                    requestTimeoutWaitHandle.Set();
                }
                else
                {
                    return;
                }

                if (deviceConnectionInfo != null)
                {
                    // Creating data table and specifying schema
                    DataTable kinectDeviceConnectivityInfo = new DataTable();

                    kinectDeviceConnectivityInfo.Columns.Add(new DataColumn("DeviceID", System.Type.GetType("System.String")));
                    kinectDeviceConnectivityInfo.Columns.Add(new DataColumn("DeviceName", System.Type.GetType("System.String")));
                    kinectDeviceConnectivityInfo.Columns.Add(new DataColumn("DeviceStatus", System.Type.GetType("System.String")));
                    kinectDeviceConnectivityInfo.Columns.Add(new DataColumn("DeviceLocked", System.Type.GetType("System.String")));

                    // Populating data table from the list data sent by kinect service
                    foreach (KinectDeviceInfo deviceInfo in deviceConnectionInfo)
                    {
                        kinectDeviceConnectivityInfo.Rows.Add(new string[] { deviceInfo.DeviceID
                        , deviceInfo.DeviceName, deviceInfo.KinectDeviceStatus.ToString()
                        , deviceInfo.DeviceLocked.ToString()});
                    }

                    // Launching service clients connectivity form in separte thread
                    ThreadPool.QueueUserWorkItem(new WaitCallback(LaunchKinectDeviceConnectivityInfo), kinectDeviceConnectivityInfo);
                }
                else
                {
                    this.SetMenuItemsStatus(true);
                    // log/show error message
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in remotingClient_evtDeviceConnectivityInfoEvent(). {0}", exp.Message); }

        }

        /// <summary>
        ///  Occurs when connectivity status sent by kinect service
        /// </summary>
        /// <param name="connectivityStatus">Connectivity status</param>
        /// <param name="connectedDeviceCount">Connected device count</param>
        /// <param name="connectedClientCount">Connected service client count</param>
        /// <param name="errorMessage">Error occurred in request processing</param>
        void remotingClient_ConnectionStatusInfoEvent(ConnectivityStatus connectivityStatus, int connectedDeviceCount, int connectedClientCount
            , String errorMessage)
        {
            try
            {
                if (errorMessage != null)
                {
                    this.Log("remotingClient_ConnectionStatusInfoEvent() Invoked! : Command response from remoting server, error message: {0}", errorMessage);


                    return;
                }

                #region Preparing Notification area tooltip

                string notifyingToolTip = Program.SysTrayName + " ( [" + connectedDeviceCount + "] Device Connected, ["
                    + connectedClientCount + "] Client Session )";

                if (connectedDeviceCount > 1 && connectedClientCount > 1)
                {
                    notifyingToolTip = Program.SysTrayName + " ( [" + connectedDeviceCount + "] Devices Connected, ["
                    + connectedClientCount + "] Client Sessions )";
                }
                else
                    if (connectedDeviceCount > 1)
                    {
                        notifyingToolTip = Program.SysTrayName + " ( [" + connectedDeviceCount + "] Devices Connected, ["
                        + connectedClientCount + "] Client Session )";
                    }
                    else
                        if (connectedDeviceCount > 1)
                        {
                            notifyingToolTip = Program.SysTrayName + " ( [" + connectedDeviceCount + "] Device Connected, ["
                            + connectedClientCount + "] Client Sessions )";
                        }

                #endregion

                switch (connectivityStatus)
                {
                    case ConnectivityStatus.BothConnected:
                        ChangeApplicationStatusIcon(ApplicationStatusIcon.BothConnected, notifyingToolTip);
                        break;
                    case ConnectivityStatus.BothDisconnected:
                        ChangeApplicationStatusIcon(ApplicationStatusIcon.BothDisconnected, notifyingToolTip);
                        break;
                    case ConnectivityStatus.OnlyDeviceConnected:
                        ChangeApplicationStatusIcon(ApplicationStatusIcon.DeviceConnected, notifyingToolTip);
                        break;
                    case ConnectivityStatus.OnlyServiceClientConnected:
                        ChangeApplicationStatusIcon(ApplicationStatusIcon.ClientConnected, notifyingToolTip);
                        break;
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in remotingClient_ConnectionStatusInfoEvent(). {0}", exp.Message); }

          
        }

        /// <summary>
        /// Occurs when notification is sent by kinect service
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Notification Event Args</param>
        void remotingClient_ShowNotificationReceived(object sender, NotificationReceivedArgs e)
        {
            try
            {
                switch (e.NotificationType)
                {
                    case NotificationType.Exception:
                        ShowBalloonTip(BALLOON_TIP_EXCEPTION, e.Message, ToolTipIcon.Error);
                        break;
                    case NotificationType.DataExtractorStarted:
                        ShowBalloonTip(BALLOON_TIP_DATA_EXTRACTOR, e.Message, ToolTipIcon.Info);
                        break;
                    case NotificationType.DataExtractorStopped:
                        ShowBalloonTip(BALLOON_TIP_DATA_EXTRACTOR, e.Message, ToolTipIcon.Warning);
                        break;
                    case NotificationType.Kinect_Already_In_Use:
                        ShowBalloonTip(BALLOON_TIP_KINECT_DEVICE, e.Message, ToolTipIcon.Warning);
                        break;
                    case NotificationType.KinectLocked:
                        ShowBalloonTip(BALLOON_TIP_KINECT_DEVICE, e.Message, ToolTipIcon.Info);
                        break;
                    case NotificationType.KinectReleased:
                        ShowBalloonTip(BALLOON_TIP_KINECT_DEVICE, e.Message, ToolTipIcon.Info);
                        break;
                    case NotificationType.KinectStatusChanged:
                        ShowBalloonTip(BALLOON_TIP_KINECT_DEVICE, e.Message, ToolTipIcon.Info);
                        break;
                    case NotificationType.SkeletonTracked:
                        ShowBalloonTip(BALLOON_TIP_SKELETON, e.Message, ToolTipIcon.Info);
                        break;
                    case NotificationType.SkeletonReleased:
                        ShowBalloonTip(BALLOON_TIP_SKELETON, e.Message, ToolTipIcon.Info);
                        break;
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in remotingClient_ShowNotificationReceived(). {0}", exp.Message); }
        }

        /// <summary>
        /// Occurs when remoting client sends connection status to check connection is alive or not
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        void remotingClient_ServerConnectionStatus(object sender, EventArgs e)
        {
            try
            {
                // Reloads the application
                this.LoadApplicationSettings();

                // Update the remoting settings
                this.remotingClient.Initlialize(this.clientName, this.loginName
                    , this.password, this.serverURI, this.portNo, this.connectionStatusInterval);

                if (e == null) 
                {
                    // Remoting client is not connected
                    ChangeApplicationStatusIcon(ApplicationStatusIcon.ServerDisconnected, string.Empty);

                    // Connecting remoting client
                    this.remotingClient.Connect();
                }
                else
                { 
                    // Sending request to kinect service to send connectivity status
                    remotingClient.SendCommand(ClientCommands.SendConnectivityStatus);
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in remotingClient_ServerConnectionStatus(). {0}", exp.Message); }

        }

        #endregion

        #region Timer Events

        /// <summary>
        /// This event is fired to refresh the status of Kinect service
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        void updateKinectServiceStatus_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                // Stops timer to avoid re occuring of event
                updateKinectServiceStatus.Stop();

                RefreshServiceStatus();
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in updateKinectServiceStatus_Elapsed(). {0}", exp.Message); }
            finally
            {
                // Update the timer interval
                updateKinectServiceStatus.Interval = this.kinectServiceStatusInterval * 1000;

                // Starts timer
                updateKinectServiceStatus.Start();
            }
        }

        #endregion

        #region Application Event Handlers
        /// <summary>
        /// Occurs on application thread exception
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            this.Log(e.Exception, "Exception occurred in Application_ThreadException(). {0}", e.Exception.Message);

        }

        /// <summary>
        ///  Occurs on unhandled exception
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            try
            {
                Exception ex = e.ExceptionObject as Exception;
                if (ex == null) return;

                this.Log(ex, "Exception occurred in CurrentDomain_UnhandledException(). {0}", ex.Message);
            }
            catch { }
        }
        #endregion

        #region Form Overridden Methods

        /// <summary>
        /// Occurs on form loading
        /// </summary>
        /// <param name="e">Event Args</param>
        protected override void OnLoad(EventArgs e)
        {
            // Hide form window.
            this.Visible = false;

            // Remove from taskbar.
            this.ShowInTaskbar = false; 

            base.OnLoad(e);
        }

        #endregion

        #region Form Control Events

        /// <summary>
        /// Occurs when form is closing
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Arguments</param>
        private void ServiceManager_FormClosing(object sender, FormClosingEventArgs e)
        {
            SetMenuItemsStatus(false);

            if (MessageBox.Show("Are you sure, you want to close application?", Program.ApplicationName
               , MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No)
            {
                // avoiding application to shut down
                e.Cancel = true;

                SetMenuItemsStatus(true);
            }
        }

        /// <summary>
        /// Occurs when exit menu item clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void menuItemExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Occurs when show notification history menu item clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void menuItemShowNotificationHistory_Click(object sender, EventArgs e)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                using (NotificationsHistory notificationsHistory = new NotificationsHistory(notificationHistoryTable))
                {
                    // Registering event to get notify about clients performed event
                    notificationsHistory.ClearNotifications
                        += new EventHandler(notificationsHistory_ClearNotifications);

                    notificationsHistory.ShowDialog();
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in menuItemShowNotificationHistory_Click(). {0}", exp.Message); }

            finally { SetMenuItemsStatus(true); }
        }

        /// <summary>
        /// Occurs when show kinect service clients menu item is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void menuItemShowServiceClientsStatus_Click(object sender, EventArgs e)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                // Setting client request type 
                remoteRequestStatus = RemoteRequestStatus.RequestInProgress_ServiceClient;

                // Sending client request
                remotingClient.SendCommand(ClientCommands.SendClientsConnectivity);

                // Waiting for service response or timeout
                requestTimeoutWaitHandle.WaitOne(this.clientRequestTimeoutInterval * 1000);

                if (remoteRequestStatus == RemoteRequestStatus.RequestInProgress_ServiceClient)
                {
                    // expires the request
                    remoteRequestStatus = RemoteRequestStatus.NotRequested;
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in menuItemShowServiceClientsStatus_Click(). {0}", exp.Message); }

            finally { SetMenuItemsStatus(true); }
        }

        /// <summary>
        /// Occurs when show kinect device connectivity status menu item clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void menuItemDeviceConnectivityStatus_Click(object sender, EventArgs e)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                // Setting client request type 
                remoteRequestStatus = RemoteRequestStatus.RequestInProgress_KinectDevice;

                // Sending client request
                remotingClient.SendCommand(ClientCommands.SendDevicesConnectivity);

                // Waiting for service response or timeout
                requestTimeoutWaitHandle.WaitOne(this.clientRequestTimeoutInterval * 1000);

                if (remoteRequestStatus == RemoteRequestStatus.RequestInProgress_KinectDevice)
                {
                    // expires the request
                    remoteRequestStatus = RemoteRequestStatus.NotRequested;
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in menuItemDeviceConnectivityStatus_Click(). {0}", exp.Message); }

            finally { SetMenuItemsStatus(true); }
        }

        /// <summary>
        /// Occurs when service manager settings menu item is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void serviceManagerSettingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                using (ServiceManagerSettings serviceManagerSettings = new ServiceManagerSettings())
                {
                    serviceManagerSettings.ShowDialog();
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in serviceManagerSettingsToolStripMenuItem_Click(). {0}", exp.Message); }

            finally { SetMenuItemsStatus(true); }
        }

        /// <summary>
        /// Occurs when kinect service settings application menu item is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void serverSettingsApplicationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                string configApplicationPath 
                    = Path.Combine(Application.StartupPath, ServiceManager.ConfigAppExeName);

                // Launching kinect service configuration settings application
                System.Diagnostics.Process.Start(configApplicationPath);
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in serverSettingsApplicationToolStripMenuItem_Click(). {0}", exp.Message); }

        }

        /// <summary>
        /// Occurs when kinect service status menu item is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void menuItemKinectServiceStatus_Click(object sender, EventArgs e)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                ServiceController myService = new ServiceController();

                myService.ServiceName = KinectService.ThisServiceName;

                if (menuItemKinectServiceStatus.Text.Equals("&Stop Kinection Service"))
                {
                    if (myService.Status != ServiceControllerStatus.Stopped)
                    {
                        myService.Stop();
                    }
                    
                    return;
                }

                if (menuItemKinectServiceStatus.Text.Equals("&Start Kinection Service"))
                {
                    if (myService.Status != ServiceControllerStatus.Running)
                    {
                        myService.Start();                     
                    }
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in menuItemKinectServiceStatus_Click(). {0}", exp.Message); }

            finally { SetMenuItemsStatus(true); }

        }

        /// <summary>
        /// Occurs when adjust kinect device menu item is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        private void menuItemAdjustKinectDevice_Click(object sender, EventArgs e)
        {
            try
            {
                // Disable all the context menu items to avoid 
                // user performing operations simultaneously
                SetMenuItemsStatus(false);

                using (AdjustKinectDevice adjustKinectDevice = new AdjustKinectDevice())
                {
                    // Registering event to get notify about clients performed event
                    adjustKinectDevice.evtMoveDevice
                        += new AdjustKinectDevice.dlgMoveDevice(adjustKinectDevice_evtMoveDevice);

                    adjustKinectDevice.ShowDialog();
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in menuItemAdjustKinectDevice_Click(). {0}", exp.Message); }

            finally { SetMenuItemsStatus(true); }
        }

        #endregion

        #region Child Form Event Handlers

        /// <summary>
        /// Occurs when client request to clear notifications history
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Event Args</param>
        void notificationsHistory_ClearNotifications(object sender, EventArgs e)
        {
            try
            {
                lock (notificationHistoryTable)
                {
                    // Removing history of notifications sent by kinect service
                    notificationHistoryTable.Rows.Clear(); 
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in notificationsHistory_ClearNotifications(). {0}", exp.Message); }
      
        }

        /// <summary>
        /// Occurs when client request to adjust kinect device up or down
        /// </summary>
        /// <param name="moveDeviceUp">Specifies movement direction Up/Down (true/false)</param>
        void adjustKinectDevice_evtMoveDevice(bool moveDeviceUp)
        {
            try
            {
                if (remoteRequestStatus != null)
                {
                    if (moveDeviceUp)
                    {
                        // Sending command to kinect service to move device up
                        remotingClient.SendCommand(ClientCommands.MoveDeviceUp);
                    }
                    else
                    {
                        // Sending command to kinect service to move device down
                        remotingClient.SendCommand(ClientCommands.MoveDeviceDown);
                    }
                }
            }
            catch (Exception exp) { this.Log(exp, "Exception occurred in adjustKinectDevice_evtMoveDevice(). {0}", exp.Message); }

        }

        #endregion

        #region Logging Methods
        private void Log(String logFormat, params Object[] args) { this.Log(null, logFormat, args); }

        private void Log(Exception exp, String logFormat, params Object[] args)
        {
            try { Logger.Log(exp, "[ServiceManager] >> {0}", String.Format(logFormat, args)); }
            catch { }
        }
        #endregion
    }

    #region Enums

    /// <summary>
    /// Enums defines Service Manager status icons.
    /// </summary>
    internal enum ApplicationStatusIcon : int
    {
        /// <summary>
        /// Client utility connection is not established
        /// </summary>
        ServerDisconnected = 1,

        /// <summary>
        /// Service client(s) connected
        /// </summary>
        ClientConnected = 2,

        /// <summary>
        /// Kinect device(s) connected
        /// </summary>
        DeviceConnected = 3,

        /// <summary>
        /// Both service client(s) and kinect device(s) are connected
        /// </summary>
        BothConnected = 4,

        /// <summary>
        /// Both service client(s) and kinect device(s) are disconnected
        /// </summary>
        BothDisconnected = 5
    }

    /// <summary>
    /// Enums defines the Remote request statuses.
    /// </summary>
    internal enum RemoteRequestStatus : int
    {
        /// <summary>
        /// Client utility didn't requested for anything
        /// </summary>
        NotRequested = 1,

        /// <summary>
        /// Client utility requested and request response received
        /// </summary>
        RequestFullFilled = 2,

        /// <summary>
        /// Client utility requested for list of connected service clients and request response not received yet
        /// </summary>
        RequestInProgress_ServiceClient = 3,

        /// <summary>
        /// Client utility requested for list of connected kinect devices and request response not received yet
        /// </summary>
        RequestInProgress_KinectDevice = 4,
    }

    #endregion
}


